package projectManagementServer;


import java.io.*;
import java.net.*;
import java.util.*;

import javax.swing.JOptionPane;

import messages.*;

public class ServerLogic{
	private ArrayList <ClientThread>al;
	private ArrayList<ChatRoom> allRoom;
	ArrayList<String> userOnline;
	private ChatRoom lobby;
	private ServerGUI serverGUI;
	private int port;
	private boolean running;
	
	AccessDatabase adb;

	public ServerLogic(ServerGUI serverGUI) throws Exception{
		 this.serverGUI = serverGUI;
	 	 port = 2500;
		 al = new ArrayList <>();
		 
		 lobby = new ChatRoom("lobby");
		 allRoom = new ArrayList<>();
		 userOnline = new ArrayList<>();
		 allRoom.add(lobby);
		 adb = new AccessDatabase();
		 for(Project x : adb.getAllProject()){
			 allRoom.add(new ChatRoom(x.getProjectName()));
		 }
	}
	
	public void start(){
	 	running = true;
		try{	
			 ServerSocket serverSocket = new ServerSocket(port);
			
			while (running){
				System.out.println("Server is waiting for client");
				Socket socket = serverSocket.accept();
					
				if(!running)
					break;
				
				ClientThread t = new ClientThread(socket);
				al.add(t);;
				t.start();
			}
				
			try{
				serverSocket.close();
					
				for(int i = 0;i<al.size();i++){
					ClientThread tc = al.get(i);
					try{
						tc.sInput.close();
						tc.sOutput.close();
						tc.socket.close();
					}catch(Exception e){}
	       		} 	
			}catch (Exception e){
				e.printStackTrace();
			}
  			}catch (Exception e){
	  			e.printStackTrace();
  			}
	}

	public synchronized void removeUserLogIn (String userName){
		for(int i=0; i<al.size(); i++){
			if(al.get(i).getUserName().equals(userName)){
 				al.remove(al.lastIndexOf(al.get(i)));
 			}
		}
		
		for(int x=0; x<allRoom.size(); x++){
			if(allRoom.get(x).getMemberList().contains(userName)){
				allRoom.get(x).getMemberList().remove(userName);
			}
		}
 		//allRoom.get(0).removeMember(userName);
	}
	
	protected void stop(){
		if(al.size() != 0){
			for(int i=0; i<al.size(); i++){
				Message msg = new ServerStopMessage();
				al.get(i).writeMsg(msg);
			
			}
		}else{}
		
		al = new ArrayList<>();
		allRoom = new ArrayList<>();
		
		try{
			running = false;
			new Socket("localhost", port);
		}catch (Exception e){e.printStackTrace();}
    }
   
	public synchronized void broadcast (Message msg){
		for(int i=0; i<allRoom.size(); i++){
			if(allRoom.get(i).getRoomName().equalsIgnoreCase(msg.getChatRoom())){
				allRoom.get(i).addMessage(msg);
				sendSpecificMsg(allRoom.get(i).getMemberList(), msg);
			}
		}
	}
					
	public synchronized void sendSpecificMsg(ArrayList<String> members, Message msg){
		for(int i=0; i<al.size(); i++){
			for(int x=0; x<members.size(); x++){
				if(members.get(x).equalsIgnoreCase(al.get(i).getUserName())){
					al.get(i).writeMsg(msg);
				}
			}
		}
	}
	
	public synchronized void switchRoom(String userName, String roomName, String originProj) throws Exception{
		ArrayList<Announcement> allAnnouncement = new ArrayList<>();
		AccessDatabase acdb = new AccessDatabase();
		allAnnouncement = acdb.getAllAnnnouncement();
		
		if(originProj != null){
			for(int i=0; i<allRoom.size(); i++){
				if(allRoom.get(i).getRoomName().equalsIgnoreCase(originProj)){
					allRoom.get(i).getMemberList().remove(userName);
				}
			}
		}else{}
		
		for(int i=0; i<allRoom.size(); i++){
			if(allRoom.get(i).getRoomName().equalsIgnoreCase(roomName)){
				allRoom.get(i).addMember(userName);
				for(int x=0; x<al.size(); x++){
					if(al.get(x).getUserName().equalsIgnoreCase(userName)){
						al.get(x).writeMsg(new ClearDiscussionMessage());
						for(int y=0; y<allRoom.get(i).getAllMessage().size(); y++){
							al.get(x).writeMsg(allRoom.get(i).getAllMessage().get(y));
						}
					}
				}
			}
		}
		
		for(ChatRoom t : allRoom){
			if(t.getRoomName().equalsIgnoreCase(roomName)){
				UpdateMessage updateMsg = new UpdateMessage("memberList");
				updateMsg.newMemberList(t.getMemberList());
				for(int i=0; i<al.size(); i++){
					for(int c=0; c<t.getMemberList().size(); c++){
						if(t.getMemberList().get(c).equalsIgnoreCase(al.get(i).getUserName())){
							if(!t.getMemberList().get(c).equalsIgnoreCase(userName)){
								AddSingleMemberToList singleMem = new AddSingleMemberToList();
								singleMem.addMember(userName);
								al.get(i).writeMsg(singleMem);
							}else{
								al.get(i).writeMsg(updateMsg);
							}
						}
					}
				}
				//sendSpecificMsg(t.getMemberList(), updateMsg);
			}
		}
		
		for(int i=0; i<al.size(); i++){
			if(al.get(i).getUserName().equalsIgnoreCase(userName)){
				al.get(i).writeMsg(new AnnouncementMessage(allAnnouncement, "get"));
			}
		}
	}
	
	public synchronized void appendToGUI(Message msg){
		serverGUI.appendMsg(msg);
	}
	
	public ArrayList<String> getOnlineUser(){
		ArrayList<String> onlineUsers = new ArrayList<>();
		
		for(ClientThread x : al){
			onlineUsers.add(x.getUserName());
		}
		
		return onlineUsers;
	}

/******************************************************************** CLIENT THREAD **************************************************************/
	
	public class ClientThread extends Thread{
		Socket socket;
		ObjectInputStream sInput;
		ObjectOutputStream sOutput;
	
		DiscussionMessage discussionMessage;
		Message message;
		String userName;
		boolean running;
		public ClientThread(Socket socket){
			this.socket = socket;
			
			try{
				sOutput = new ObjectOutputStream(socket.getOutputStream());
				sInput = new ObjectInputStream(socket.getInputStream());
			}catch(Exception e){}
		}
// 	RUN THREAD
//  READ INPUT

		@Override
		public void run(){
			running = true;
			while(running){
				try{
					message = (Message)sInput.readObject();
					
					switch(message.getType()){
						case Message.logInKey:
							userName = message.getUser();
							verifyLogIn(message);
							break;
							
						case Message.discussionKey:
							broadcast(message);
							break;
						
						case Message.connectKey:
							ConnectMessage nm = (ConnectMessage) message;
							switchRoom(userName, nm.getDestProj(), nm.getOriginProject());
							break;
							
						case Message.addMemberKey:
							addMemberToDataBase(message);
							break;
							
						case Message.logOutKey:
							userOnline.remove(userName);
							removeUserLogIn(userName);
							running = false;
							break;
						
						case Message.deleteMemberKey:
							deleteMemberToDatabase(message);
							break;
							
						case Message.completedProject:
							setCompletedProject(message);
							break;
							
						case Message.ongoingProject:
							setOngoingProject(message);
							break;
							
						case Message.fileKey:
							setFileToDatabase(message);
							break;
						
						case Message.announcement:
							verifyAnnouncement(message);
							break;
							
					}
				}catch(Exception e){}
					
			}
			//removeUserLogIn(userName);
			close();
		}
		
//CLOSE THREAD

		public void close(){
			running = false;
			try{
				if(sOutput !=null) sOutput.close();
				if(sInput != null)sInput.close();
				if(socket !=null)socket.close();
			}catch(Exception e){e.printStackTrace();}
		}

// SEND MESSAGE

		private synchronized boolean writeMsg(Message msg){
			
			if(!socket.isConnected()){
				close();
				return false;
			}
			try{
				sOutput.writeObject(msg);
			}catch(Exception e){}
			return true;
		}
		
// GET NAME ---------------------------------------------------------------------------------------------------------------------------------------
		public String getUserName(){
			return userName;
		}
// VERIFY LOG IN -----------------------------------------------------------------------------------------------------------------------------------
		public void verifyLogIn(Message msg) throws Exception{
			LogInMessage logInMsg = (LogInMessage) msg;
			int occurence = 0;
			AccessDatabase db = new AccessDatabase();
			
			AllDataList allData = new AllDataList();
			allData.setAllMember(db.getAllMember());
			allData.setMemProj(db.getAllMemProj());			
			allData.setAllProject(db.getAllProject());
			allData.setFiles(db.getAllFiles());
			
			
			
			for(Member x : db.getAllMember()){
				if(logInMsg.getUser().equals(x.getUserName()) & logInMsg.getPassword().equals(x.getPassword())){
					if(x.getStatus().equalsIgnoreCase("DEACTIVATE")){
						Message err = new ErrorLogInMessage("DEACTIVE");
						writeMsg(err);
						al.remove(this);
						occurence++;
						close();
					}
					
					if(x.getStatus().equalsIgnoreCase("ACTIVE")){
						if(userOnline.contains(userName)){
							Message err = new ErrorLogInMessage("DUPLICATE LOG IN");
							writeMsg(err);
							al.remove(this);
							occurence++;
							close();
						}else{
							appendToGUI(message);
							writeMsg(new LobbyMessage());
							writeMsg(allData);
							switchRoom(userName, "lobby", null);
							userOnline.add(userName);
							occurence++;
						}
						
					}
					break;
				}
				if(x.getUserName().equalsIgnoreCase(logInMsg.getUser()) & !(x.getPassword().equalsIgnoreCase(logInMsg.getPassword()))){
					Message err = new ErrorLogInMessage("INCORRECT PASSWORD");
					writeMsg(err);
					al.remove(this);
					occurence++;
					close();
					break;
				}
				if(x.getUserName().equalsIgnoreCase(logInMsg.getUser())){
					occurence++;
				}	
			}
			if(occurence == 0){
				Message err = new ErrorLogInMessage("NOT REGISTERED");
				writeMsg(err);
				al.remove(this);
				close();
			}
		}
		
		public void addMemberToDataBase(Message msg) throws Exception{
			AddMemberMessage addMember = (AddMemberMessage) msg;
			AccessDatabase adb = new AccessDatabase();
			adb.addMemberProject(addMember.getMemberToAdd(), addMember.getProject());
			
		}
		
		public void deleteMemberToDatabase(Message msg) throws Exception{
			DeleteMemberMessage dmm = (DeleteMemberMessage) msg;
			AccessDatabase adb = new AccessDatabase();
			adb.deleteMemberProject(dmm.getMemberToDelete(), dmm.getProject());
		}
		
		public void setCompletedProject(Message msg) throws Exception{
			CompletedMessage message = (CompletedMessage) msg;
			serverGUI.setCompletedProject(message.getProjectName());
			AccessDatabase adb = new AccessDatabase();
			adb.updateProjectStatusCompleted(message.getProjectName());
		}
		
		public void setOngoingProject(Message msg) throws Exception{
			OngoingMessage message = (OngoingMessage) msg;
			serverGUI.setOngoingProject(message.getProjectName());
			AccessDatabase adb = new AccessDatabase();
			adb.updateProjectStatusOngoing(message.getProjectName());
		}
		
		public void setFileToDatabase(Message msg) throws Exception{
			FileMessage file = (FileMessage) msg;
			AccessDatabase adb = new AccessDatabase();
			adb.addFile(file.getFileName(), file.getAuthor(), file.getFileContent(), file.getFileType(), file.getDate(), file.getProjectName());
		}
		
		public void verifyAnnouncement(Message msg) throws Exception{
			AccessDatabase acd = new AccessDatabase();
			AnnouncementMessage annMsg = (AnnouncementMessage) msg;
			switch(annMsg.getCommand()){
			case "add":
				acd.addAnnouncement(annMsg.getAllAnnouncement().get(0).getProjectName(), annMsg.getAllAnnouncement().get(0).getTitle(), annMsg.getAllAnnouncement().get(0).getMessage(), annMsg.getAllAnnouncement().get(0).getDate());
				break;
			
			case "DEL":
				acd.delAnnouncement(annMsg.getAllAnnouncement().get(0).getTitle());
				break;
				
			case "edit":
				acd.updateAnnouncement(annMsg.getAllAnnouncement().get(0));
				break;
			}
		}
	}


	public class ChatRoom{
		volatile ArrayList<String> members;
		volatile ArrayList<DiscussionMessage> allMessage;
		String roomName;
		
		public ChatRoom(String roomName){
			members = new ArrayList<>();
			allMessage = new ArrayList<>();
			this.roomName = roomName;
		}
		
		public void addMember(String userName){
			members.add(userName);
		}
		
		public void addMessage(Message msg){
			allMessage.add((DiscussionMessage) msg);
		}
		
		public void removeMember(String userName){
			for(String x : members){
				if(x.equals(userName)){
					members.remove(x);
				}
			}
		}
		
		public String getRoomName(){
			return roomName;
		}
		
		public ArrayList<String> getMemberList(){
			return members;
		}
		
		public ArrayList<DiscussionMessage> getAllMessage(){
			return allMessage;
		}
	}
 	
 	
 	
}